package net.sansi.v3correctserver.service.correct;

import net.sansi.v3correctserver.entity.IntLine;
import net.sansi.v3correctserver.entity.IntPoint;
import net.sansi.v3correctserver.entity.screen.CorrectArea;
import net.sansi.v3correctserver.entity.screen.Rect;
import net.sansi.v3correctserver.service.photorec.rec.RecRectScreen;

/**
 * Created by Fang
 * create time : 2024/11/7
 * IDE : IntelliJ IDEA
 * <p>
 * 矫正算法基类，提供一些公共工具
 */

public class RectCorrectBase {

    protected int span, offsetX, offsetY;
    protected int showWidth, showHeight;
    protected Rect recSize;

    /**
     * 初始化参数
     *
     * @param showWidth  打屏宽
     * @param showHeight 打屏高
     * @param span       隔点
     */
    protected void initBaseParams(
            int showWidth, int showHeight, int span
    ) {
        this.span = span;
        this.showWidth = showWidth;
        this.showHeight = showHeight;
    }

    /**
     * 设置偏移量
     *
     * @param offsetX x偏移
     * @param offsetY y偏移
     */
    public void setOffset(int offsetX, int offsetY) {
        this.offsetX = offsetX;
        this.offsetY = offsetY;
    }

    /**
     * 校验识别结果行列是否合法
     *
     * @param rec 识别结果
     * @return 是否合法
     */
    protected boolean isRecSizeValid(RecRectScreen rec) {
        return rec.getMatrix().getWidth() == recSize.getWidth() && rec.getMatrix().getHeight() == recSize.getHeight();
    }

    /**
     * 将mxy坐标转换为结果坐标
     *
     * @param mx mx坐标
     * @param my my 坐标
     * @return 结果坐标
     */
    protected IntPoint mxyToRxy(int mx, int my) {
        return new IntPoint(
                mx * span + offsetX,
                my * span + offsetY
        );
    }

    /**
     * 是否在区域内
     *
     * @param point 点
     * @param rect  区域
     * @return 是否在区域内
     */
    protected boolean isInRect(IntPoint point, Rect rect) {
        int x = point.getX(), y = point.getY();
        return x >= rect.getLeft() && x <= rect.getRight() && y >= rect.getTop() && y <= rect.getBottom();
    }

    /**
     * 将结果坐标 转换为 mxy坐标
     *
     * @param rx 结果坐标 x
     * @param ry 结果坐标 y
     * @return mxy坐标
     */
    protected IntPoint rxyToMxy(int rx, int ry) {
        return new IntPoint(
                (rx - offsetX) / span,
                (ry - offsetY) / span
        );
    }


    /**
     * 在某个方向上，rect1 和 rect2 是否相邻
     *
     * @param rect1 区域1
     * @param rect2 区域2
     * @param dir   方向 [ 上， 右， 下， 左 ]
     * @return 是否相邻
     */
    protected boolean isNeighbor(Rect rect1, Rect rect2, int dir) {
        if (dir == 0) {
            return rect1.getTop() == rect2.getBottom() + 1 && rect1.getLeft() == rect2.getLeft() && rect1.getRight() == rect2.getRight();
        } else if (dir == 1) {
            return rect1.getRight() == rect2.getLeft() - 1 && rect1.getTop() == rect2.getTop() && rect1.getBottom() == rect2.getBottom();
        } else if (dir == 2) {
            return rect1.getBottom() == rect2.getTop() - 1 && rect1.getLeft() == rect2.getLeft() && rect1.getRight() == rect2.getRight();
        } else {
            return rect1.getLeft() == rect2.getRight() + 1 && rect1.getTop() == rect2.getTop() && rect1.getBottom() == rect2.getBottom();
        }
    }

    /**
     * 在某个方向上，rect1 和 rect2 是否相邻
     *
     * @param area1 区域1
     * @param area2 区域2
     * @param dir   方向 [ 上， 右， 下， 左 ]
     * @return 是否相邻
     */
    protected boolean isNeighborArea(CorrectArea area1, CorrectArea area2, int dir) {
        return isNeighbor(area1.getValidArea(), area2.getValidArea(), dir);
    }

    /**
     * 检查两个区域是否相邻
     *
     * @param rect1 区域1
     * @param rect2 区域2
     * @return 是否相邻
     */
    protected boolean isNeighbor(Rect rect1, Rect rect2) {
        return isNeighbor(rect1, rect2, 0) || isNeighbor(rect1, rect2, 1) || isNeighbor(rect1, rect2, 2) || isNeighbor(rect1, rect2, 3);
    }

    /**
     * 是否在边界上
     *
     * @param line 线条
     * @param rect 区域
     * @return 是否在边界上
     */
    protected boolean isOnBoundary(IntLine line, Rect rect) {
        return isOneLine(line, rect.getLine(0)) || isOneLine(line, rect.getLine(1)) || isOneLine(line,
                rect.getLine(2)) || isOneLine(line, rect.getLine(3));
    }

    /**
     * 检查两条直线是否在一条直线上
     * 这两条直线只有两个方向， 水平或垂直
     *
     * @param line1 线条1
     * @param line2 线条2
     * @return 是否在一条直线上
     */
    protected boolean isOneLine(IntLine line1, IntLine line2) {
        int dir1 = lineDir(line1), dir2 = lineDir(line2);
        if ((dir1 & dir2) == 0) return false;
        int dir = Math.min(dir1, dir2);
        if (dir == 0x01) {
            if (line1.p1.getX() != line2.p1.getX()) return false;
            int top = Math.max(Math.min(line1.p1.getY(), line1.p2.getY()), Math.min(line2.p1.getY(), line2.p2.getY()));
            int bottom = Math.min(Math.max(line1.p1.getY(), line1.p2.getY()), Math.max(line2.p1.getY(),
                    line2.p2.getY()));
            return bottom >= top;
        } else if (dir == 0x11) {
            if (line1.p1.getY() != line2.p1.getY()) return false;
            int left = Math.max(Math.min(line1.p1.getX(), line1.p2.getX()), Math.min(line2.p1.getX(), line2.p2.getX()));
            int right = Math.min(Math.max(line1.p1.getX(), line1.p2.getX()), Math.max(line2.p1.getX(),
                    line2.p2.getX()));
            return right >= left;
        }
        return line1.p1.equals(line2.p1);
    }

    /**
     * 获取线条的方向
     *
     * @param line 线条
     * @return 线条的方向
     */
    protected int lineDir(IntLine line) {
        int[] p1 = line.p1.toArray(), p2 = line.p2.toArray();
        if (p1[0] == p2[0] && p1[1] != p2[1]) return 0x01;
        if (p1[0] != p2[0] && p1[1] == p2[1]) return 0x10;
        return 0x11;
    }

    /**
     * 是否存在交集
     *
     * @param area1 区域1
     * @param area2 区域2
     * @return 是否存在交集
     */
    public boolean isHaveIntersection(Rect area1, Rect area2) {
        int l1 = area1.getLeft(), t1 = area1.getTop(), r1 = area1.getRight(), b1 = area1.getBottom();
        int l2 = area2.getLeft(), t2 = area2.getTop(), r2 = area2.getRight(), b2 = area2.getBottom();
        return l1 <= r2 && l2 <= r1 && t1 <= b2 && t2 <= b1;
    }

    /**
     * 获取两个区域的交集
     *
     * @param area1 区域1
     * @param area2 区域2
     * @return 交集
     */
    public Rect computeIntersection(Rect area1, Rect area2) {
        int l1 = area1.getLeft(), t1 = area1.getTop(), r1 = area1.getRight(), b1 = area1.getBottom();
        int l2 = area2.getLeft(), t2 = area2.getTop(), r2 = area2.getRight(), b2 = area2.getBottom();
        int l = Math.max(l1, l2), t = Math.max(t1, t2), r = Math.min(r1, r2), b = Math.min(b1, b2);
        if (l > r || t > b) return null;
        return new Rect(l, t, r - l + 1, b - t + 1);
    }
}
